package com.xiaoma.chat.webservice;

import io.rong.ApiHttpClient;
import io.rong.models.FormatType;
import io.rong.models.SdkHttpResult;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.dexcoder.assistant.utils.UUIDUtils;
import com.xiaoma.chat.domain.ChatGroup;
import com.xiaoma.chat.domain.ChatRm;
import com.xiaoma.chat.domain.GroupUser;
import com.xiaoma.chat.domain.User;
import com.xiaoma.chat.dto.CreateRelationshipDto;
import com.xiaoma.chat.dto.GroupDto;
import com.xiaoma.chat.dto.HistoryMessageDto;
import com.xiaoma.chat.dto.QuitGroupDto;
import com.xiaoma.chat.dto.RelationshipDto;
import com.xiaoma.chat.dto.UserGroupDto;
import com.xiaoma.chat.dto.UserMetaData;
import com.xiaoma.chat.resource.IMConstants;
import com.xiaoma.chat.resource.Organization;
import com.xiaoma.chat.resource.Role;
import com.xiaoma.chat.service.GroupService;
import com.xiaoma.chat.service.HistoryService;
import com.xiaoma.chat.service.UserService;
import com.xiaoma.chat.vo.MessageRecordVo;
import com.xiaoma.chat.vo.UserVo;

@Controller
@RequestMapping("/service")
public class IMController {

    private Logger LOG = LoggerFactory.getLogger(IMController.class);
    
    @Autowired
    private UserService userService;
    @Autowired
    private GroupService groupService;
    @Autowired
    private HistoryService historyService;
    
    @RequestMapping(value="/token", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> getToken(@RequestBody User userDto){
        Map<String, Object> result = new HashMap<String, Object>();
        if(userDto.validate()){
            User user = userService.findUser(userDto.getName(), userDto.getOrganizationId());
            if (null == user) {
            	try {
            		user = insertUser(userDto);
            	} catch(Exception e) {
            	    LOG.error(e.getMessage());
            		result.put("status", 0);
                    result.put("message", e.getMessage());
                    return result;
            	}
            } else {
                user.setNickname(userDto.getNickname());
                user.setPortraitUri(userDto.getPortraitUri());
                userService.updateUser(user);
            }
            result.put("status", 1);
            result.put("user", user);
        } else {
            result.put("status", 0);
            result.put("message", "validate faild");
        }
        return result;
    }
    
    @RequestMapping(value="/group/create", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> createGroup(@RequestBody GroupDto groupDto) {
    	Map<String, Object> result = new HashMap<String, Object>();
    	if (groupDto.validate()) {
    	    if (groupService.findGroupByName(groupDto.getName(), groupDto.getOrganizationId()) == null) {
    	        try {
                    int ownerId = groupDto.getOwnerId();
                    String imGroupId = UUIDUtils.getUUID32();
                    User owner = userService.getById(ownerId);
                    if (owner != null && owner.getOrganizationId().intValue() == Organization.TEACHER.orgId().intValue()) {
                        List<UserMetaData> teacherMetadatas = groupDto.getTeacherMetaDatas();
                        List<UserMetaData> adminMetadatas = groupDto.getAdminMetaDatas();
                        List<String> imUserIds = new ArrayList<String>();
                        List<UserVo> userVos = new ArrayList<UserVo>();
                        Set<Integer> userSet = new HashSet<Integer>();//用来去重
                        imUserIds.add(owner.getImUserId());
                        UserVo ownerVo = new UserVo();
                        ownerVo.build(owner, Role.ADMIN.code());
                        userVos.add(ownerVo);
                        userSet.add(ownerVo.getId());
                        for (UserMetaData adminMeta: adminMetadatas) {
                            if (adminMeta.validate()) {
                                User user = insertUser(adminMeta,Organization.TEACHER.orgId());
                                if (!userSet.contains(user.getId())) {
                                    imUserIds.add(user.getImUserId());
                                    UserVo userVo = new UserVo();
                                    userVo.build(user, Role.ADMIN.code());
                                    userVos.add(userVo);
                                    userSet.add(userVo.getId());
                                }
                            }
                        }
                        
                        for (UserMetaData teacherMeta: teacherMetadatas) {
                            if (teacherMeta.validate()) {
                                User user = insertUser(teacherMeta,Organization.TEACHER.orgId());
                                if (!userSet.contains(user.getId())) {
                                    imUserIds.add(user.getImUserId());
                                    UserVo userVo = new UserVo();
                                    userVo.build(user, Role.TEACHER.code());
                                    userVos.add(userVo);
                                    userSet.add(userVo.getId());
                                }
                            }
                        }
                        
                        SdkHttpResult shResult = ApiHttpClient.createGroup(IMConstants.key, IMConstants.secret, imUserIds, imGroupId, groupDto.getName(), FormatType.json);
                        if (shResult.getHttpCode() != 200) {
                            throw new RuntimeException("remote exception code: " + shResult.getHttpCode());
                        }
                        //create group & userGroup
                        ChatGroup group = new ChatGroup();
                        group.setImGroupId(imGroupId);
                        group.setName(groupDto.getName());
                        group.setDescription(groupDto.getDescription());
                        group.setOrganizationId(groupDto.getOrganizationId());
                        group.setOwnerId(groupDto.getOwnerId());
                        group.setType(groupDto.getType());
                        int id = groupService.insertGroup(group);
                        group.setId(id);
                        
                        for (UserVo userVo: userVos) {
                            GroupUser groupUser = new GroupUser();
                            groupUser.setGroupId(group.getId());
                            groupUser.setUserId(userVo.getId());
                            groupUser.setRole(userVo.getRole());
                            groupService.saveGroupUser(groupUser);
                        }
                        
                        result.put("status", 1);
                        result.put("group", group);
                        result.put("users", userVos);
                    } else {
                        result.put("status", 0);
                        result.put("message", "invalid owner");
                    }
                } catch(Exception e) {
                    LOG.error(e.getMessage());
                    result.put("status", 0);
                    result.put("message", e.getMessage());
                }
    	    } else {
    	        result.put("status", 0);
                result.put("message", "group name exist");
    	    }
    	} else {
    		result.put("status", 0);
            result.put("message", "validate faild");
    	}
    	return result;
    }
    
    @RequestMapping(value="/group/dismiss", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> dismissGroup(@RequestBody UserGroupDto userGroupDto) {
        Map<String, Object> result = new HashMap<String, Object>();
        if (userGroupDto.validate()) {
            
            User user = userService.getById(userGroupDto.getUserId());
            ChatGroup group = groupService.getById(userGroupDto.getGroupId());
            if (user != null && group != null) {
                if (user.getId().intValue() == group.getOwnerId().intValue()) {
                    
                    try {
                        SdkHttpResult shResult = ApiHttpClient.dismissGroup(IMConstants.key, IMConstants.secret, user.getImUserId(), group.getImGroupId(), FormatType.json);
                        if (shResult.getHttpCode() == 200) {
                            groupService.destroyGroup(userGroupDto.getGroupId());
                            result.put("status", 1);
                        } else {
                            result.put("status", 0);
                            result.put("message", "remote exception code: " + shResult.getHttpCode());
                        }
                    } catch(Exception e) {
                        LOG.error(e.getMessage());
                        result.put("status", 0);
                        result.put("message", "destroy group error");
                    }
                    
                } else {
                    result.put("status", 0);
                    result.put("message", "only owner of this group can dismiss");
                }
            } else {
                result.put("status", 0);
                result.put("message", "user or group is not found");
            }
        } else {
            result.put("status", 0);
            result.put("message", "validate error");
        }
        return result;
        
    }
    
    @RequestMapping(value="/group/join", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> joinGroup(@RequestBody UserGroupDto userGroupDto) {
        Map<String, Object> result = new HashMap<String, Object>();
        if (userGroupDto.validate()) {
            User user = userService.getById(userGroupDto.getUserId());
            ChatGroup group = groupService.getById(userGroupDto.getGroupId());
            if (user != null && group != null) {
                if (user.getOrganizationId().intValue() == group.getOrganizationId().intValue()) {
                    try {
                        SdkHttpResult shResult = ApiHttpClient.joinGroup(IMConstants.key, IMConstants.secret, user.getImUserId(), group.getImGroupId(), group.getName(), FormatType.json);
                        if (shResult.getHttpCode() == 200) {
                            GroupUser groupUser = groupService.findGroupUser(userGroupDto.getUserId(), userGroupDto.getGroupId());
                            if (groupUser == null) {
                                groupUser = new GroupUser();
                                groupUser.setGroupId(userGroupDto.getGroupId());
                                groupUser.setUserId(userGroupDto.getUserId());
                                groupUser.setRole(Role.STUDENT.code());
                                groupService.saveGroupUser(groupUser);
                            }
                            result.put("status", 1);
                            result.put("group", group);
                            result.put("users", groupService.findUsersByGroupId(userGroupDto.getGroupId()));
                        } else {
                            result.put("status", 0);
                            result.put("message", "remote exception code: " + shResult.getHttpCode());
                        }
                    } catch(Exception e) {
                        LOG.error(e.getMessage());
                        result.put("status", 0);
                        result.put("message", "join group error: " + e.getMessage());
                    }
//                    if (groupService.findGroupUserCount(userGroupDto.getGroupId()) < 100) {
//                    } else {
//                        result.put("status", 0);
//                        result.put("message", "join group error, group users over 100");
//                    }
                } else {
                    result.put("status", 0);
                    result.put("message", "can't join other orgnization group");
                }
            } else {
                result.put("status", 0);
                result.put("message", "user or group is not found");
            }
        } else {
            result.put("status", 0);
            result.put("message", "validate error");
        }
        return result;
    }
    
    @RequestMapping(value="/group/quit", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> quitGroup(@RequestBody QuitGroupDto quitGroupDto) {
        Map<String, Object> result = new HashMap<String, Object>();
        if (quitGroupDto.validate()) {
            GroupUser opGroupUser = groupService.findGroupUser(quitGroupDto.getOperatorId(), quitGroupDto.getGroupId());
            ChatGroup group = groupService.getById(quitGroupDto.getGroupId());
            if (opGroupUser != null && group != null) {
                Integer ownerId = group.getOwnerId();
                if (opGroupUser.getRole().intValue() == Role.ADMIN.code().intValue()) {//only admin can quit user
                    List<String> imUserIds = new ArrayList<String>(quitGroupDto.getUserIds().size());
                    for (Integer userId: quitGroupDto.getUserIds()) {
                        User user = userService.getById(userId);
                        if (user != null && user.getId() != ownerId) {
                            imUserIds.add(user.getImUserId());
                        }
                    }
                    try {
                        SdkHttpResult shResult = ApiHttpClient.quitGroupBatch(IMConstants.key, IMConstants.secret, imUserIds, group.getImGroupId(), FormatType.json);
                        if (shResult.getHttpCode() == 200) {
                            quitGroupDto.getUserIds().remove(ownerId);
                            groupService.quitGroup(quitGroupDto.getGroupId(), quitGroupDto.getUserIds());
                            result.put("status", 1);
                            result.put("group", group);
                            result.put("users", groupService.findUsersByGroupId(quitGroupDto.getGroupId()));
                        } else {
                            result.put("status", 0);
                            result.put("message", "remote exception code: " + shResult.getHttpCode());
                        }
                    } catch (Exception e) {
                        LOG.error(e.getMessage());
                        result.put("status", 0);
                        result.put("message", "quit group error");
                    }
                } else {
                    result.put("status", 0);
                    result.put("message", "only admin can quit user");
                }
            } else {
                result.put("status", 0);
                result.put("message", "operator or group donesn't exist");
            }
        } else {
            result.put("status", 0);
            result.put("message", "validate failed");
        }
        return result;
    }
    
    @RequestMapping(value="/group/all", method = RequestMethod.GET)
    @ResponseBody
    public Map<String, Object> listGroups(@RequestParam Integer organizationId) {
        Map<String, Object> result = new HashMap<String, Object>();
        if (Organization.isExist(organizationId)) {
            result.put("status", 1);
            result.put("groups", groupService.findGroupsByOrgId(organizationId));
        } else {
            result.put("status", 0);
            result.put("message", "invalid organizationId");
        }
        return result;
    }
    
    @RequestMapping(value="/group/{groupId}/users", method = RequestMethod.GET)
    @ResponseBody
    public Map<String, Object> listGroupMembers(@PathVariable Integer groupId) {
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("users", groupService.findUsersByGroupId(groupId));
        result.put("status", 1);
        return result;
    }
    
    @RequestMapping(value="/group/me/{userId}", method = RequestMethod.GET)
    @ResponseBody
    public Map<String, Object> listMyGroups(@PathVariable Integer userId) {
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("myGroups", groupService.findGroupUsers(userId));
        result.put("status", 1);
        return result;
    }
    
    
    @RequestMapping(value="/relationship", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> relationship(@RequestBody CreateRelationshipDto createRelationshipDto) {
        Map<String, Object> result = new HashMap<String, Object>();
        Integer teacherId = createRelationshipDto.getTeacherId();
        Integer studentId = createRelationshipDto.getStudentId();
        Integer groupId = createRelationshipDto.getGroupId();
        Integer sendId = createRelationshipDto.getSendId();
        if (createRelationshipDto.validate()) {
            GroupUser teacher = groupService.findGroupUser(teacherId, groupId);
            GroupUser student = groupService.findGroupUser(studentId, groupId);
            if (sendId.intValue() == studentId.intValue()) {//发起者是学生
                if (student == null) {
                    result.put("status", 0);
                    result.put("message", "you have been quit in this group");
                    return result;
                }
                if (teacher == null) {
                    result.put("status", 0);
                    result.put("message", "the teacher has been quit in this group");
                    return result;
                    
                }
                try {
                    userService.deleteChatRm(studentId, groupId);
                    ChatRm chatRm = new ChatRm();
                    chatRm.setStudentId(studentId);
                    chatRm.setTeacherId(teacherId);
                    chatRm.setGroupId(groupId);
                    userService.saveChatRm(chatRm);
                    result.put("status", 1);
                } catch(Exception e) {
                    LOG.error(e.getMessage());
                    result.put("status", 0);
                    result.put("message", e.getMessage());
                }
                
            } else {//发起者是老师
                if (student == null) {
                    result.put("status", 0);
                    result.put("message", "the student has been quit in this group");
                    return result;
                }
                if (teacher == null) {
                    result.put("status", 0);
                    result.put("message", "you have been quit in this group");
                    return result;
                    
                }
                List<ChatRm> chatRms = userService.findChatRms(null, studentId, groupId);
                if (chatRms.size() == 0) {
                    ChatRm chatRm = new ChatRm();
                    chatRm.setStudentId(studentId);
                    chatRm.setTeacherId(teacherId);
                    chatRm.setGroupId(groupId);
                    userService.saveChatRm(chatRm);
                    result.put("status", 1);
                } else {
                    result.put("status", 0);
                    result.put("message", "student has been assigned");
                }
            }
        } else {
            result.put("status", 0);
            result.put("message", "validate failed");
        }
        return result;
    }
    
    
    @RequestMapping(value="/relationship", method = RequestMethod.GET)
    @ResponseBody
    public Map<String, Object> getRelationship(RelationshipDto relationshipDto) {
        Map<String, Object> result = new HashMap<String, Object>();
        if (relationshipDto.getGroupId() != null) {
            List<ChatRm> chatRms = userService.findChatRms(relationshipDto.getTeacherId(), relationshipDto.getStudentId(), relationshipDto.getGroupId());
            result.put("status", 1);
            result.put("relationship", chatRms);
        } else {
            result.put("status", 0);
            result.put("message", "groupId is null");
        }
        return result;
    }
    
    
    @RequestMapping(value="/history/messages", method = RequestMethod.GET)
    @ResponseBody
    public Map<String, Object> historyMessages(HistoryMessageDto messageDto) {
        Map<String, Object> result = new HashMap<String, Object>();
        if (messageDto.validate()) {
            List<MessageRecordVo> messageRecods = historyService.findMessages(messageDto);
            result.put("status", 1);
            result.put("messageRecods", messageRecods);
        } else {
            result.put("status", 0);
            result.put("message", "validate faild");
        }
        return result;
    }
    
    
    private User insertUser(User userDto) throws Exception {
        String imUserId = UUIDUtils.getUUID32();
        String portraitUri = StringUtils.isEmpty(userDto.getPortraitUri()) ? "xxx" : userDto.getPortraitUri();
        userDto.setPortraitUri(portraitUri);
        SdkHttpResult shResult = ApiHttpClient.getToken(IMConstants.key, IMConstants.secret, imUserId, userDto.getName(), userDto.getPortraitUri(), FormatType.json);
        if (shResult.getHttpCode() != 200) {
        	throw new RuntimeException("remote exception code: " + shResult.getHttpCode());
        }
        JSONObject json = JSONObject.fromObject(shResult.getResult());
        String token = json.getString("token");
        userDto.setToken(token);
        userDto.setCreatedAt(new Date()); 
        userDto.setImUserId(imUserId);
        Integer id  = userService.insertUser(userDto);
        userDto.setId(id);
        return userDto;
    }
    
    private User insertUser(UserMetaData metaData, int organizationId) throws Exception {
        User user = userService.findUser(metaData.getName(), organizationId);
        if (null == user) {
            User userDto = new User();
            userDto.setName(metaData.getName());
            userDto.setNickname(metaData.getNickname());
            userDto.setPortraitUri(metaData.getPortraitUri());
            userDto.setOrganizationId(organizationId);
            user = insertUser(userDto);
        }
        return user;
    }
    
}
